﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GnufBase.h"

namespace gnuf {

    // Observer Pattern
    template<class Observer>
    class Subject
    {
    public:
        enum class PriorityLevel
        {
            eLevel_1 = 1,
            eLevel_2,
            eLevel_3,
            eLevel_4,
            eLevel_5,
        };

        enum class Channel
        {
            eInputChannel,      // 输入通道
            eOutputChannel,     // 输出通道
            eBoardcastChannel   // 广播通道
        };

        virtual ~Subject() {
            m_mapInputObs.clear();
            m_mapOutputObs.clear();
            m_mapBoardcastObs.clear();
        }

    public:
        void subscribe(Observer* obs, Channel channel = Channel::eBoardcastChannel, PriorityLevel priority = PriorityLevel::eLevel_3)
        {
            if (obs == nullptr) return;
            switch (channel)
            {
            case Channel::eInputChannel: m_mapInputObs[priority].emplace(obs); break;
            case Channel::eOutputChannel: m_mapOutputObs[priority].emplace(obs); break;
            case Channel::eBoardcastChannel: m_mapBoardcastObs[priority].emplace(obs); break;
            default:break;
            }
        }
        void unsubscribe(Observer* obs, Channel channel = Channel::eBoardcastChannel)
        {
            switch (channel)
            {
            case Channel::eInputChannel: unsubscribe(obs, m_mapInputObs); break;
            case Channel::eOutputChannel: unsubscribe(obs, m_mapOutputObs); break;
            case Channel::eBoardcastChannel: unsubscribe(obs, m_mapBoardcastObs); break;
            default:break;
            }
        }

        // type of Func is std::bind()
        template<typename Func>
        void notifyEvent(const Func& func, Observer* trigger = nullptr, Channel channel = Channel::eBoardcastChannel)
        {
            switch (channel)
            {
            case Channel::eInputChannel: notify(func, trigger, m_mapInputObs); break;
            case Channel::eOutputChannel: notify(func, trigger, m_mapOutputObs); break;
            case Channel::eBoardcastChannel: notify(func, trigger, m_mapBoardcastObs); break;
            default:break;
            }
        }

    protected:
        Subject() {}

    private:
        template<typename Func>
        void notify(const Func& func, Observer* trigger, const std::map<PriorityLevel, std::set<Observer*>>& mapObs)
        {
            for (auto& iter : mapObs)
            {
                for (auto& obs : iter.second)
                {
                    if (trigger != obs)
                    {
                        if(obs) func(obs);
                    }
                }
            }
        }

        void unsubscribe(Observer* obs, std::map<PriorityLevel, std::set<Observer*>>& mapObs)
        {
            for (auto& iter : mapObs)
            {
                if (!iter.second.empty())
                {
                    iter.second.erase(obs);
                }
            }
        }

    private:
        std::map<PriorityLevel, std::set<Observer*>> m_mapInputObs;
        std::map<PriorityLevel, std::set<Observer*>> m_mapOutputObs;
        std::map<PriorityLevel, std::set<Observer*>> m_mapBoardcastObs;
    };
}
